//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Positioning stuff.
//

# include "StdAfx.h"
# include "Tracking.h"
# include "Positioning.h"
# include "MpsFile.h"
# include "Map.h"
# include "MapView.h"
# include "Types.h"
# include "FileWriter.h"
# include "fast_floor.h"
# include "Wrappers.h"
# include "Globals.h"


static HANDLE g_hTrackFile;
static CFileWriter * g_pFileWriter;
static size_t g_cTrackNumber;
static size_t g_cTrackOffset;
static size_t g_cPoints;
static size_t g_cPointsOffset;

static file_ref_t * g_pFileRef = NULL;
static rgn_t * g_pTrackRgn = NULL;

static void FixTrackHdr ();

void StartTracking () {
	if (g_pFileWriter)
		StopTracking ();

	// Get folder with mapedit.exe.
	char strProcessName [MAX_PATH + 1];
	::GetModuleFileName (NULL, strProcessName, MAX_PATH);
	char * const pPathEnd = ::strrchr (strProcessName, '\\');
	if (pPathEnd)
		* pPathEnd = '\0';

	// Make sure that 'Tracks' folder exists.
	char strTracksFolder [MAX_PATH];
	::sprintf (strTracksFolder, "%s\\Tracks", strProcessName);
	if (::GetFileAttributes (strTracksFolder) != FILE_ATTRIBUTE_DIRECTORY) {
		::CreateDirectory (strTracksFolder, NULL);
	}

	// Get current time.
	SYSTEMTIME lt;
	::GetLocalTime (& lt);

	// Prepare name of the track file.
	char strFileName [MAX_PATH + 1];
	::sprintf (strFileName, "%s\\Tracks\\%d-%02d-%02d_%02d-%02d-%02d.mps", strProcessName, lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);

	// TODO: add suffix "(N)" if file already exists.
	CFileForWrite hTrackFile (strFileName);
	if (! hTrackFile.IsValid ()) {
		ErrorMsgBox ("Can't create file to write tracks:\n %s", strFileName);
		return;
	}
	g_hTrackFile = hTrackFile.Detach ();
	g_pFileWriter = new CFileWriter (g_hTrackFile);

	// Write the header.
	g_pFileWriter->WriteBin (g_pMpsHeader, c_cMpsHeaderSize);

	g_cTrackNumber = 0;

	// Prepare to hot visualization.
	g_map.file_refs.push_back (file_ref_t ());
	g_pFileRef = & g_map.file_refs.back ();

	g_pFileRef->bAttachment = true;
	g_pFileRef->bDeleted    = false;
	g_pFileRef->bVisible    = true;
	g_pFileRef->strFileName = strFileName;
	
	StartNewTrack ();
}

void StopTracking () {
	if (g_pFileWriter == NULL)
		return;

	// Update header of the last track.
	FixTrackHdr ();

	// Save tail record.
	assert (g_pFileWriter);
	g_pFileWriter->WriteBin (g_pMpsTail, c_cMpsTailSize);

	g_pFileWriter->FlushBuffer ();

	// Close file.
	delete g_pFileWriter;
	g_pFileWriter = NULL;

	::CloseHandle (g_hTrackFile);
	g_hTrackFile = NULL;
}

void StartNewTrack () {
	if (g_pFileWriter == NULL)
		return;

	if (g_cTrackNumber > 0 && g_cPoints == 0)
		return;

	if (g_cTrackNumber > 0)
		FixTrackHdr ();

	// Prepare the name of the track.
	char strTrackName [32];
	::sprintf (strTrackName, "Track %03d", g_cTrackNumber);
	++ g_cTrackNumber;

	if (g_pFileRef) {
		g_pTrackRgn = & g_map.CreateRgn (rkPolyline);
		g_pTrackRgn->SetTrackType ();
		g_pTrackRgn->pFileRef = g_pFileRef;
		g_pTrackRgn->strLabel = strTrackName;

		g_pTrackRgn->elements.push_back (rgn_element_t ());
		rgn_element_t & element = g_pTrackRgn->elements.back ();
		element.cLevel   = 0;
		element.bDeleted = false;
	}

	// Save 'size' field offset to fix it later.
	assert (g_pFileWriter);
	g_cTrackOffset = g_pFileWriter->GetPos ();
	g_pFileWriter->WriteDWORD (1000*sizeof (CMpsTrackPoint));

	g_pFileWriter->WriteBYTE ('T');   // Track

	g_pFileWriter->WriteBin (strTrackName, ::strlen (strTrackName) + 1);
	g_pFileWriter->WriteBYTE (0);     // btDisplayOnGPS = no
	g_pFileWriter->WriteDWORD (0);    // dwColor = black

	// Save 'points' field offset to fix it later.
	g_cPointsOffset = g_pFileWriter->GetPos ();
	g_pFileWriter->WriteDWORD (1000); // dwPoints = 1000 (in advance)
	g_cPoints = 0;
}

static void FixTrackHdr () {
	assert (g_pFileWriter);
	const size_t cEnd = g_pFileWriter->GetPos ();

	// Save true size of the record.
	g_pFileWriter->SetPos (g_cTrackOffset);
	g_pFileWriter->WriteDWORD (cEnd - g_cTrackOffset - sizeof (CMpsRecordHdr));

	// Save true number of points.
	g_pFileWriter->SetPos (g_cPointsOffset);
	g_pFileWriter->WriteDWORD (g_cPoints);

	g_pFileWriter->SetPos (cEnd);
}

void AddPointToTrack (const PositionInfo_t & _pi) {
	if (g_pFileWriter == NULL)
		return;

	if ((_pi.dwDef & PositionInfo_t::defXY) == 0)
		return;

	++ g_cPoints;

	CMpsTrackPoint tp;
	::memset (& tp, 0, sizeof (tp));

	tp.lY = fast_floor_d (_pi.fY*(1UL << 31)/180.f + .5f);
	tp.lX = fast_floor_d (_pi.fX*(1UL << 31)/180.f + .5f);

	if (_pi.dwDef & PositionInfo_t::defAltitude) {
		tp.btHasAltitude = 1;
		tp.dblAltitude   = _pi.fAltitude;
	}

	if (_pi.dwDef & PositionInfo_t::defTime) {
		tp.btHasTime  = 1;
		// In seconds elapsed since 1 Jan 1970
		tp.dwDateTime = fast_floor_d ((_pi.dtTime - 25569.)*24.*60.*60. + .5);
	}

	if (_pi.dwDef & PositionInfo_t::defDepth) {
		tp.btHasDepth = 1;
		tp.dblDepth   = _pi.fDepth;
	}

	assert (g_pFileWriter);
	g_pFileWriter->WriteBin (& tp, sizeof (tp));

	// Visualize new point.
	if (g_pTrackRgn) {
		rgn_element_t & element = g_pTrackRgn->elements [0];
		element.points.push_back (point_t (_pi.fX, _pi.fY));

		g_pTrackRgn->RefreshAttributes (g_map);

		// TODO: invoke it from GUI thread.
		DelayedRefresh (500);
	}
}
